<template>
  <div class="suspense-async-demo">
    <h2>Vue 3 Suspense 与异步组件</h2>
    <p class="description">探索 Vue 3 中的 Suspense 功能和异步组件的高级用法</p>

    <!-- 控制区 -->
    <div class="demo-controls">
      <div class="async-controls">
        <label>选择组件类型:</label>
        <select v-model="selectedComponent" class="demo-select">
          <option value="basic">基础异步组件</option>
          <option value="withError">带错误处理的组件</option>
          <option value="timeout">超时测试组件</option>
          <option value="concurrent">并发加载组件</option>
        </select>
      </div>
      <div class="demo-buttons">
        <button @click="loadComponent" class="demo-button">加载组件</button>
        <button @click="resetDemo" class="demo-button secondary">重置演示</button>
        <button @click="toggleSuspense" class="demo-button">
          {{ useSuspense ? '禁用 Suspense' : '启用 Suspense' }}
        </button>
      </div>
    </div>

    <!-- 性能指标 -->
    <div class="performance-metrics">
      <div class="metric">
        <span class="metric-label">加载时间:</span>
        <span class="metric-value">{{ loadTime }}ms</span>
      </div>
      <div class="metric">
        <span class="metric-label">状态:</span>
        <span class="metric-value" :class="statusClass">{{ componentStatus }}</span>
      </div>
    </div>

    <!-- 演示区域 -->
    <div class="demo-area">
      <!-- 使用 Suspense -->
      <template v-if="useSuspense">
        <Suspense>
          <template #default>
            <component 
              :is="currentAsyncComponent" 
              :key="componentKey"
              @loaded="onComponentLoaded"
              @error="onComponentError"
            />
          </template>
          <template #fallback>
            <div class="fallback-content">
              <div class="loading-spinner"></div>
              <p>组件正在加载中...</p>
              <p class="loading-details">{{ loadingMessage }}</p>
            </div>
          </template>
        </Suspense>
      </template>

      <!-- 不使用 Suspense -->
      <template v-else>
        <div v-if="isLoading" class="manual-loading">
          <div class="loading-spinner"></div>
          <p>手动处理加载状态...</p>
        </div>
        <div v-else-if="hasError" class="manual-error">
          <div class="error-icon">⚠️</div>
          <p>{{ errorMessage }}</p>
          <button @click="retryLoad" class="demo-button small">重试</button>
        </div>
        <component 
          v-else 
          :is="currentAsyncComponent" 
          :key="componentKey"
          @loaded="onComponentLoaded"
        />
      </template>
    </div>

    <!-- 错误处理区域 -->
    <div v-if="errorInfo" class="error-details">
      <h3>错误详情</h3>
      <pre>{{ errorInfo }}</pre>
    </div>

    <!-- Suspense 原理说明 -->
    <div class="suspense-info">
      <h3>Suspense 工作原理</h3>
      <div class="info-content">
        <ul>
          <li><strong>异步边界:</strong> Suspense 创建一个异步边界，可以等待内部异步依赖解析</li>
          <li><strong>默认插槽:</strong> 包含可能会挂起的异步组件</li>
          <li><strong>fallback 插槽:</strong> 当默认插槽中的内容挂起时显示的备用内容</li>
          <li><strong>错误处理:</strong> 通常与 errorCaptured 或 onErrorCaptured 结合使用</li>
          <li><strong>嵌套使用:</strong> Suspense 可以嵌套以处理多个级别的异步加载</li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch, onErrorCaptured, nextTick } from 'vue';

// 演示状态
const selectedComponent = ref('basic');
const useSuspense = ref(true);
const isLoading = ref(false);
const hasError = ref(false);
const errorMessage = ref('');
const errorInfo = ref('');
const loadTime = ref(0);
const componentKey = ref(0);
const loadingMessage = ref('');
const componentStatus = ref('就绪');

// 异步组件定义
const AsyncBasicComponent = defineAsyncComponent(() => {
  return new Promise((resolve) => {
    loadingMessage.value = '加载基础组件...';
    // 模拟网络延迟
    setTimeout(() => {
      resolve({
        template: `
          <div class="async-basic-component">
            <h3>基础异步组件</h3>
            <p>这个组件通过 defineAsyncComponent API 异步加载。</p>
            <div class="component-content">
              <div class="feature-list">
                <div class="feature-item">
                  <span class="feature-icon">✨</span>
                  <span>按需加载</span>
                </div>
                <div class="feature-item">
                  <span class="feature-icon">📦</span>
                  <span>代码分割</span>
                </div>
                <div class="feature-item">
                  <span class="feature-icon">⚡</span>
                  <span>性能优化</span>
                </div>
              </div>
            </div>
          </div>
        `
      });
    }, 1500);
  });
});

const AsyncErrorComponent = defineAsyncComponent({
  loader: () => {
    loadingMessage.value = '加载可能失败的组件...';
    return new Promise((_, reject) => {
      // 模拟随机错误
      const shouldFail = Math.random() > 0.5;
      setTimeout(() => {
        if (shouldFail) {
          reject(new Error('组件加载失败！这是一个模拟的错误场景。'));
        } else {
          resolve({
            template: `
              <div class="async-error-component">
                <h3>带错误处理的组件</h3>
                <p>这个组件有时会成功加载，有时会失败。</p>
                <div class="success-message">
                  <span class="success-icon">✅</span>
                  组件加载成功！
                </div>
              </div>
            `
          });
        }
      }, 2000);
    });
  },
  // 错误组件
  errorComponent: {
    template: `
      <div class="error-component">
        <h3>加载错误</h3>
        <p>组件加载过程中遇到错误。</p>
        <button @click="$emit('retry')" class="retry-button">重试</button>
      </div>
    `,
    emits: ['retry']
  },
  // 延迟
  delay: 200,
  // 超时
  timeout: 5000
});

const AsyncTimeoutComponent = defineAsyncComponent({
  loader: () => {
    loadingMessage.value = '加载超时测试组件...';
    return new Promise((resolve) => {
      // 故意设置较长的延迟
      setTimeout(() => {
        resolve({
          template: `
            <div class="async-timeout-component">
              <h3>超时测试组件</h3>
              <p>这个组件加载时间较长，可能会触发超时。</p>
              <div class="timeout-warning">
                <span class="warning-icon">⏰</span>
                加载时间超过预期
              </div>
            </div>
          `
        });
      }, 6000);
    });
  },
  timeout: 3000
});

const AsyncConcurrentComponent = defineAsyncComponent(() => {
  loadingMessage.value = '加载并发组件（模拟多个API请求）...';
  
  // 模拟多个并发请求
  const fetchUserData = () => new Promise(resolve => setTimeout(() => resolve({ name: 'Vue User', id: 1 }), 1000));
  const fetchStatsData = () => new Promise(resolve => setTimeout(() => resolve({ views: 1000, likes: 50 }), 1200));
  const fetchContentData = () => new Promise(resolve => setTimeout(() => resolve({ title: '并发加载演示', content: '这是通过多个并发请求加载的数据' }), 800));
  
  return Promise.all([fetchUserData(), fetchStatsData(), fetchContentData()])
    .then(([userData, statsData, contentData]) => {
      // 等待所有数据加载完成后返回组件
      return {
        template: `
          <div class="async-concurrent-component">
            <h3>并发加载组件</h3>
            <p>这个组件展示了如何并发加载多个资源。</p>
            
            <div class="data-sections">
              <div class="data-section">
                <h4>用户信息</h4>
                <p>用户名: {{ userData.name }}</p>
                <p>用户ID: {{ userData.id }}</p>
              </div>
              
              <div class="data-section">
                <h4>统计数据</h4>
                <p>浏览量: {{ statsData.views }}</p>
                <p>点赞数: {{ statsData.likes }}</p>
              </div>
              
              <div class="data-section">
                <h4>内容数据</h4>
                <p>标题: {{ contentData.title }}</p>
                <p>内容: {{ contentData.content }}</p>
              </div>
            </div>
          </div>
        `,
        setup() {
          return {
            userData,
            statsData,
            contentData
          };
        }
      };
    });
});

// 计算当前要加载的组件
const currentAsyncComponent = computed(() => {
  switch (selectedComponent.value) {
    case 'basic': return AsyncBasicComponent;
    case 'withError': return AsyncErrorComponent;
    case 'timeout': return AsyncTimeoutComponent;
    case 'concurrent': return AsyncConcurrentComponent;
    default: return AsyncBasicComponent;
  }
});

// 计算状态样式类
const statusClass = computed(() => {
  if (isLoading.value) return 'status-loading';
  if (hasError.value) return 'status-error';
  if (loadTime.value > 0) return 'status-success';
  return 'status-ready';
});

// 加载组件
function loadComponent() {
  resetState();
  componentKey.value++;
  isLoading.value = true;
  componentStatus.value = '加载中';
  const startTime = performance.now();
  
  // 如果不使用Suspense，手动处理异步状态
  if (!useSuspense) {
    // 模拟异步加载过程
    Promise.resolve()
      .then(() => {
        // 异步组件加载由Vue处理，这里只是为了演示
        return nextTick();
      })
      .catch(error => {
        handleError(error);
      });
  }
}

// 组件加载成功回调
function onComponentLoaded() {
  const endTime = performance.now();
  loadTime.value = Math.round(endTime - startTime);
  isLoading.value = false;
  hasError.value = false;
  componentStatus.value = '加载成功';
  errorMessage.value = '';
}

// 组件错误回调
function onComponentError(error) {
  handleError(error);
}

// 处理错误
function handleError(error) {
  isLoading.value = false;
  hasError.value = true;
  componentStatus.value = '加载失败';
  errorMessage.value = error?.message || '发生未知错误';
  errorInfo.value = error?.stack || String(error);
}

// 重置演示
function resetDemo() {
  resetState();
  componentKey.value++;
}

// 重置状态
function resetState() {
  isLoading.value = false;
  hasError.value = false;
  loadTime.value = 0;
  errorMessage.value = '';
  errorInfo.value = '';
  componentStatus.value = '就绪';
  loadingMessage.value = '';
}

// 重试加载
function retryLoad() {
  loadComponent();
}

// 切换Suspense使用状态
function toggleSuspense() {
  useSuspense.value = !useSuspense.value;
  resetDemo();
}

// 错误捕获
onErrorCaptured((error, instance, info) => {
  console.log('捕获到错误:', error);
  console.log('错误信息:', info);
  
  // 只有在不使用Suspense时手动处理错误
  if (!useSuspense.value) {
    handleError(error);
    return false; // 阻止错误继续传播
  }
  
  return true; // 允许错误继续向上传播
});
</script>

<style scoped>
.suspense-async-demo {
  max-width: 1000px;
  margin: 0 auto;
  padding: 20px;
}

h2 {
  color: #333;
  margin-bottom: 10px;
}

.description {
  color: #666;
  margin-bottom: 30px;
  font-size: 16px;
}

/* 控制区样式 */
.demo-controls {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  flex-wrap: wrap;
  gap: 20px;
  background: #f8f9fa;
  padding: 15px;
  border-radius: 8px;
}

.async-controls {
  display: flex;
  align-items: center;
  gap: 10px;
}

.async-controls label {
  font-weight: 500;
  color: #333;
}

.demo-select {
  padding: 8px 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 14px;
  min-width: 200px;
  background: white;
  cursor: pointer;
}

.demo-buttons {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.demo-button {
  padding: 8px 16px;
  background: #42b883;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  transition: background 0.3s;
}

.demo-button:hover {
  background: #35495e;
}

.demo-button.secondary {
  background: #64748b;
}

.demo-button.small {
  padding: 6px 12px;
  font-size: 12px;
}

/* 性能指标样式 */
.performance-metrics {
  display: flex;
  gap: 30px;
  margin-bottom: 20px;
  padding: 15px;
  background: #e0f2fe;
  border-radius: 8px;
}

.metric {
  display: flex;
  align-items: center;
  gap: 10px;
}

.metric-label {
  font-weight: 500;
  color: #333;
}

.metric-value {
  font-weight: bold;
  padding: 4px 8px;
  border-radius: 4px;
}

.status-loading {
  background: #fbbf24;
  color: #78350f;
}

.status-error {
  background: #fecaca;
  color: #991b1b;
}

.status-success {
  background: #dcfce7;
  color: #15803d;
}

.status-ready {
  background: #dbeafe;
  color: #1d4ed8;
}

/* 演示区域样式 */
.demo-area {
  min-height: 300px;
  margin-bottom: 20px;
  padding: 20px;
  border: 2px solid #e0e0e0;
  border-radius: 8px;
  background: #ffffff;
}

/* 加载状态样式 */
.fallback-content,
.manual-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px;
  text-align: center;
}

.loading-spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #e0e0e0;
  border-top: 4px solid #42b883;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 20px;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.loading-details {
  color: #666;
  font-size: 14px;
  margin-top: 10px;
}

/* 错误状态样式 */
.manual-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px;
  text-align: center;
  background: #fef2f2;
  border-radius: 8px;
}

.error-icon {
  font-size: 48px;
  margin-bottom: 20px;
}

.error-details {
  margin-bottom: 20px;
  padding: 15px;
  background: #fef2f2;
  border: 1px solid #fecaca;
  border-radius: 8px;
}

.error-details h3 {
  color: #991b1b;
  margin-bottom: 10px;
}

.error-details pre {
  background: #ffffff;
  padding: 10px;
  border-radius: 4px;
  overflow-x: auto;
  font-size: 12px;
  color: #666;
  max-height: 200px;
}

/* 组件样式 */
.async-basic-component,
.async-error-component,
.async-timeout-component,
.async-concurrent-component {
  padding: 20px;
  background: #f8fafc;
  border-radius: 8px;
  border: 1px solid #e2e8f0;
}

.async-basic-component h3,
.async-error-component h3,
.async-timeout-component h3,
.async-concurrent-component h3 {
  color: #333;
  margin-bottom: 15px;
}

.component-content {
  margin-top: 20px;
}

.feature-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 15px;
}

.feature-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
  background: white;
  border-radius: 6px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.feature-icon {
  font-size: 20px;
}

.success-message {
  margin-top: 20px;
  padding: 15px;
  background: #dcfce7;
  border-radius: 6px;
  display: flex;
  align-items: center;
  gap: 10px;
  color: #15803d;
}

.success-icon {
  font-size: 24px;
}

.timeout-warning {
  margin-top: 20px;
  padding: 15px;
  background: #fef3c7;
  border-radius: 6px;
  display: flex;
  align-items: center;
  gap: 10px;
  color: #92400e;
}

.warning-icon {
  font-size: 24px;
}

.data-sections {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
  margin-top: 20px;
}

.data-section {
  padding: 15px;
  background: white;
  border-radius: 6px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.data-section h4 {
  color: #333;
  margin-bottom: 10px;
}

.data-section p {
  color: #666;
  margin: 5px 0;
}

.error-component {
  padding: 20px;
  background: #fef2f2;
  border-radius: 8px;
  text-align: center;
}

.error-component h3 {
  color: #991b1b;
  margin-bottom: 15px;
}

.retry-button {
  padding: 8px 16px;
  background: #ef4444;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  margin-top: 15px;
}

/* 信息区域样式 */
.suspense-info {
  background: #f0fdf4;
  border-radius: 8px;
  padding: 20px;
  border-left: 4px solid #42b883;
}

.suspense-info h3 {
  color: #333;
  margin-bottom: 15px;
}

.info-content ul {
  color: #475569;
  line-height: 1.6;
  margin: 0;
  padding-left: 20px;
}

.info-content li {
  margin-bottom: 10px;
}

.info-content strong {
  color: #333;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .demo-controls {
    flex-direction: column;
    align-items: stretch;
  }
  
  .async-controls {
    justify-content: space-between;
  }
  
  .demo-buttons {
    justify-content: center;
  }
  
  .performance-metrics {
    flex-direction: column;
    gap: 15px;
  }
  
  .data-sections {
    grid-template-columns: 1fr;
  }
}
</style>